package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

func main() {

}

type Limiter struct {
	Max     int32
	Current int32
	Cond    *sync.Cond
	Lock    sync.Mutex
	Closed  int32
}

func NewLimiter(max int32) *Limiter {
	return &Limiter{
		Max:     max,
		Current: 0,
		Cond:    sync.NewCond(&sync.Mutex{}),
		Closed:  0,
	}
}

func (l *Limiter) Acquire() {
	l.Lock.Lock()
	defer l.Lock.Unlock()
	if atomic.LoadInt32(&l.Closed) == 1 {
		panic("limiter is closed")
	}

	if atomic.LoadInt32(&l.Current) >= l.Max {
		l.Cond.Wait()
	}

	atomic.AddInt32(&l.Current, 1)
}
func (l *Limiter) Release() {
	l.Lock.Lock()
	defer l.Lock.Unlock()
	if atomic.LoadInt32(&l.Closed) == 1 {
		panic("limiter is closed")
	}
	ncount := atomic.AddInt32(&l.Current, -1)
	if ncount < l.Max {
		l.Cond.Signal()
	}
}

func (l *Limiter) Close() {
	l.Lock.Lock()
	defer l.Lock.Unlock()
	atomic.StoreInt32(&l.Closed, 1)
	l.Cond.Broadcast()
}

func worker(id int, l *Limiter, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Printf("worker acquire: id=%d start,limiter max=%d, current=%d \n", id, l.Max, l.Current)
	l.Acquire()
	time.Sleep(1 * time.Second)
	fmt.Printf("handle task 1 sec")
	l.Release()
	fmt.Printf("worker release: id=%d start,limiter max=%d, current=%d \n", id, l.Max, l.Current)
}
